'use strict';
const { Service } = require('egg');
const dayjs = require('dayjs');

/**
 * 失败任务记录服务
 * 记录和管理队列中失败的任务
 * @class FailedJobService
 * @extends {Service}
 */
class FailedJobService extends Service {
  /**
   * 记录失败的任务
   * @param {string} queueName - 队列名称
   * @param {Object} job - 任务对象
   * @param {Error} error - 错误对象
   * @returns {Promise<Object>} 记录结果
   */
  async recordFailedJob(queueName, job, error) {
    const { ctx } = this;
    try {
      // 提取关键信息
      const failedJob = {
        queue_name: queueName,
        job_id: job.id,
        job_name: job.name,
        job_data: JSON.stringify(job.data),
        error_message: error.message,
        error_stack: error.stack || '',
        attempts_made: job.attemptsMade,
        max_attempts: job.opts.attempts,
        failed_at: new Date(),
        created_at: new Date(),
        updated_at: new Date()
      };

      // 保存到数据库
      const result = await ctx.model.FailedJob.create(failedJob);
      ctx.logger.info(`已记录失败任务: ${queueName}-${job.id}, ID: ${result.id}`);
      return result;
    } catch (err) {
      ctx.logger.error(`记录失败任务出错:`, err);
      // 记录到文件日志作为备份
      ctx.logger.error(`[FAILED_JOB_BACKUP] queue: ${queueName}, jobId: ${job.id}, error: ${error.message}, data: ${JSON.stringify(job.data)}`);
      return null;
    }
  }

  /**
   * 获取失败任务列表
   * @param {Object} query - 查询条件
   * @param {string} [query.queueName] - 队列名称
   * @param {Date} [query.startDate] - 开始日期
   * @param {Date} [query.endDate] - 结束日期
   * @param {number} [query.page=1] - 页码
   * @param {number} [query.pageSize=20] - 每页条数
   * @returns {Promise<Object>} 任务列表和总数
   */
  async getFailedJobs(query) {
    const { ctx } = this;
    const { queueName, startDate, endDate, page = 1, pageSize = 20 } = query;

    const where = {};
    if (queueName) {
      where.queue_name = queueName;
    }
    if (startDate) {
      where.failed_at = { ...where.failed_at, [Op.gte]: startDate };
    }
    if (endDate) {
      where.failed_at = { ...where.failed_at, [Op.lte]: endDate };
    }

    const offset = (page - 1) * pageSize;
    const limit = pageSize;

    const { count, rows } = await ctx.model.FailedJob.findAndCountAll({
      where,
      order: [[ 'failed_at', 'DESC' ]],
      limit,
      offset
    });

    return {
      total: count,
      list: rows.map(row => {
        const data = row.toJSON();
        try {
          data.job_data = JSON.parse(data.job_data);
        } catch (e) {
          // 如果JSON解析失败，保持原始字符串
        }
        return data;
      }),
      page,
      pageSize,
      totalPages: Math.ceil(count / pageSize)
    };
  }

  /**
   * 重试失败的任务
   * @param {number} id - 失败任务ID
   * @returns {Promise<Object>} 重试结果
   */
  async retryFailedJob(id) {
    const { ctx } = this;
    const failedJob = await ctx.model.FailedJob.findByPk(id);
    if (!failedJob) {
      throw new Error(`失败任务不存在: ${id}`);
    }

    try {
      // 解析任务数据
      let jobData = failedJob.job_data;
      try {
        jobData = JSON.parse(jobData);
      } catch (e) {
        ctx.logger.error(`解析任务数据失败:`, e);
        throw new Error(`任务数据格式错误，无法重试`);
      }

      // 添加任务到队列
      const job = await ctx.service.queue.addJob(
        failedJob.queue_name,
        failedJob.job_name,
        jobData,
        { attempts: failedJob.max_attempts }
      );

      // 更新任务状态为已重试
      await failedJob.update({
        status: 'retried',
        retried_job_id: job.id,
        retried_at: new Date(),
        updated_at: new Date()
      });

      ctx.logger.info(`已重试失败任务: ${id}, 新任务ID: ${job.id}`);
      return { success: true, jobId: job.id };
    } catch (error) {
      ctx.logger.error(`重试失败任务${id}出错:`, error);
      throw error;
    }
  }

  /**
   * 清理过期的失败任务记录
   * @param {number} days - 保留天数
   * @returns {Promise<number>} 删除的记录数
   */
  async cleanExpiredJobs(days = 30) {
    const { ctx } = this;
    const expiredDate = dayjs().subtract(days, 'day').toDate();
    const result = await ctx.model.FailedJob.destroy({
      where: {
        failed_at: { [Op.lt]: expiredDate },
        status: 'retried'
      }
    });
    ctx.logger.info(`清理过期失败任务记录: ${result}条`);
    return result;
  }
}

module.exports = FailedJobService;